/*
 * @Author: Laputa
 * @Version: V0.0
 * @Date: 2023-12-20 17:19:55
 * @LastEditors: Laputa
 * @LastEditTime: 2024-01-24 14:23:40
 * @Description: This file provides firmware functions to manage the following
 *               functionalities of the QSPI part.
 * 
 *               QSPI default initialize
 *               QSPI initialize
 *               QSPI enabel/disable
 *               QSPI change transmission parameters
 *               Get QSPI module status
 *               QSPI DMA Operation
 *               QSPI XIP default initialize
 *               QSPI XIP initialize
 *               QSPI Standard Full Duplex initialize
 *               QSPI Standard Full Duplex read-write
 *               
 *               
 * Copyright (c) 2023 by Levetop, All Rights Reserved.
 */

#include "lt168_qspi.h"

/*******************************************************************************
 * Code
 ******************************************************************************/

/**
 * @brief Sets default values for the qspi configuration structure variable pointer.
 * @param {qspi_config_ts *} qspi_config:Pointer to a qspi configuration structure that
 *                          contains the configuration information for the specified
 *                          QSPI peripheral.
 * @return {void}
 */
void QSPI_GetDefault(qspi_config_ts *qspi_config)
{
    qspi_config->frame_format                = QSPI_Data_Frame_Format_Motorola;
    qspi_config->spi_frame_format            = QSPI_Frame_Format_Standard_SPI;
    qspi_config->transfer_mode               = QSPI_Mode_Tx_and_Rx;
    qspi_config->scph                        = QSPI_Serial_Clock_Toggles_Middle_Bit;
    qspi_config->scpol                       = QSPI_Serial_Clock_Polarity_Inactive_Low;
    qspi_config->data_frame_size             = QSPI_Data_Frame_Size_8bits;
    qspi_config->trans_type                  = QSPI_Transfer_Type_TT0;
    qspi_config->addr_l                      = QSPI_Address_Length_24Bits;
    qspi_config->inst_l                      = QSPI_Instruction_Length_8Bits;
    qspi_config->dummy_cycles                = QSPI_Wait_Cycles_8;

    qspi_config->sckdv                       = 0x2U;
    qspi_config->data_frames_number          = 0x0U;

    qspi_config->sampling_edge               = QSPI_Rx_Sampling_Edge_Negative_Edge;
    qspi_config->sampling_delay              = 0x0U;
    qspi_config->clock_stretching_capability = Enable;
    

    qspi_config->transfer_start_fifo_number  = 0x0U;
    qspi_config->transmit_fifo_threshold     = 0x7U;
    qspi_config->receive_fifo_threshold      = 0x7U;
 
    qspi_config->control_frame_size          = QSPI_Control_Frame_Size_1bits;
}

/**
 * @brief Initializes the qspi peripheral according to the specified parameters in the qspi_config.
 * @param {qspi *} qspix:Pointer to a qspi structure,where can be QSPI0,QSPI1,QSPI2.
 * @param {qspi_config_ts *} qspi_config:Pointer to a qspi configuration structure that
 *                          contains the configuration information for the specified
 *                          QSPI peripheral.
 * @return {void}
 */
void QSPI_Init(qspi *qspix, qspi_config_ts *qspi_config)
{
    uint32_t tempReg = 0U;
    Bit_Clear(qspix->SSIENR, QSPI_SSIENR_SSIC_EN_MASK);  /* must close QSPI */

    /* CTRLR0 */
    Bit_Set(tempReg, QSPI_CTRLR0_DFS(qspi_config->data_frame_size));
    Bit_Set(tempReg, QSPI_CTRLR0_FRF(qspi_config->frame_format));
    Bit_Set(tempReg, QSPI_CTRLR0_SCPH(qspi_config->scph));
    Bit_Set(tempReg, QSPI_CTRLR0_SCPOL(qspi_config->scpol));
    Bit_Set(tempReg, QSPI_CTRLR0_TMOD(qspi_config->transfer_mode));
    Bit_Set(tempReg, QSPI_CTRLR0_SLV_OE(0U));
    Bit_Set(tempReg, QSPI_CTRLR0_SRL(0U));
    Bit_Set(tempReg, QSPI_CTRLR0_SSTE(0U));
    Bit_Set(tempReg, QSPI_CTRLR0_CFS(qspi_config->control_frame_size));
    Bit_Set(tempReg, QSPI_CTRLR0_SPI_FRF(qspi_config->spi_frame_format));

    qspix->CTRLR0 = tempReg;

    /* CTRLR1 */
    qspix->CTRLR1 = 0U;
    Bit_Set(qspix->CTRLR1, QSPI_CTRLR1_NDF(qspi_config->data_frames_number));

    /* BAUDR */
    qspix->BAUDR = 0U;
    Bit_Set(qspix->BAUDR, QSPI_BAUDR_SCKDV(qspi_config->sckdv));

    /* TXFTLR */
    tempReg = 0U;
    Bit_Set(tempReg, QSPI_TXFTLR_TXFTHR(qspi_config->transfer_start_fifo_number));
    Bit_Set(tempReg, QSPI_TXFTLR_TFT(qspi_config->transmit_fifo_threshold));
    qspix->TXFTLR = tempReg;

    /* RXFTLR */
    qspix->RXFTLR = 0U;
    Bit_Set(qspix->RXFTLR, QSPI_RXFTLR_RFT(qspi_config->receive_fifo_threshold));

    /* RXSDR */
    tempReg = 0U;
    Bit_Set(tempReg, QSPI_RXSDR_RSD(qspi_config->sampling_delay));
    Bit_Set(tempReg, QSPI_RXSDR_SE(qspi_config->sampling_edge));
    qspix->RXSDR = tempReg;

    /* SPICTRLR0 */
    tempReg = 0U;
    Bit_Set(tempReg, QSPI_SPICTRLR0_TRANS_TYPE(qspi_config->trans_type));
    Bit_Set(tempReg, QSPI_SPICTRLR0_ADDR_L(qspi_config->addr_l));
    Bit_Set(tempReg, QSPI_SPICTRLR0_INST_L(qspi_config->inst_l));
    Bit_Set(tempReg, QSPI_SPICTRLR0_WAIT_CYCLES(qspi_config->dummy_cycles));


    if(qspi_config->transfer_mode == QSPI_Mode_Tx_Only)
    {
        Bit_Set(tempReg, QSPI_SPICTRLR0_CLK_STRETCH_EN(Enable));
    }
    else

        Bit_Set(tempReg, QSPI_SPICTRLR0_CLK_STRETCH_EN(qspi_config->clock_stretching_capability));
    qspix->SPICTRLR0 = tempReg;

}

/**
 * @brief QSPI Enabel or Disable. 
 * @param {qspi *} qspix:Pointer to a qspi structure,where can be QSPI0,QSPI1,QSPI2.
 * @param {FunctionState} state:where can be Enabel or Disable.
 * @return {void}
 */
void QSPI_Cmd(qspi *qspix, FunctionState state)
{
    Bit_Clear(qspix->SSIENR, QSPI_SSIENR_SSIC_EN_MASK);
    Bit_Set(qspix->SSIENR, state);
}

/* CTRLR0 */
/**
 * @brief QSPI change CTRLR0 register.
 * @param {qspi *} qspix:Pointer to a qspi structure,where can be QSPI0,QSPI1,QSPI2.
 * @param {qspi_data_frame_size_te} data_size:QSPI Data Frame Size,
 *        where can be QSPI_Data_Frame_Size_4bits,QSPI_Data_Frame_Size_8bits...QSPI_Data_Frame_Size_32bits.
 * @param {qspi_frame_format_te} spi_frame_format:QSPI Frame Format,where can be QSPI_Frame_Format_Standard_SPI,
 *        QSPI_Frame_Format_Dual_SPI,QSPI_Frame_Format_Quad_SPI.
 * @param {qspi_transfer_mode_te} transfer_mode:QSPI Transfer Mode,where can be QSPI_Mode_Tx_and_Rx,
 *        QSPI_Mode_Tx_Only,QSPI_Mode_Rx_Only.
 * 
 * @return {void}
 */
void QSPI_Change_CTRLR0(qspi *qspix, qspi_data_frame_size_te data_size, qspi_frame_format_te spi_frame_format, qspi_transfer_mode_te transfer_mode)
{
    uint32_t tempReg = qspix->CTRLR0;

    Bit_Clear(tempReg, QSPI_CTRLR0_DFS_MASK);
    Bit_Set(tempReg, QSPI_CTRLR0_DFS(data_size));

    Bit_Clear(tempReg, QSPI_CTRLR0_SPI_FRF_MASK);
    Bit_Set(tempReg, QSPI_CTRLR0_SPI_FRF(spi_frame_format));

    Bit_Clear(tempReg, QSPI_CTRLR0_TMOD_MASK);
    Bit_Set(tempReg, QSPI_CTRLR0_TMOD(transfer_mode));

    Bit_Clear(qspix->SSIENR, QSPI_SSIENR_SSIC_EN_MASK);
    qspix->CTRLR0 = tempReg;
    Bit_Set(qspix->SSIENR, QSPI_SSIENR_SSIC_EN_MASK);
}

/* CTRLR1 */
/**
 * @brief QSPI change CTRLR1 register.
 * @param {qspi *} qspix:Pointer to a qspi structure,where can be QSPI0,QSPI1,QSPI2.
 * @param {uint16_t} fifo_num:The number of qspi fifo be transferred.
 * @return {void}
 */
void QSPI_Change_CTRLR1(qspi *qspix, uint16_t fifo_num)
{
    /* how many qspi fifo number be transfer when Tx only mode or Rx only mode. */
    Bit_Clear(qspix->SSIENR, QSPI_SSIENR_SSIC_EN_MASK);
    qspix->CTRLR1 = fifo_num;
    Bit_Set(qspix->SSIENR, QSPI_SSIENR_SSIC_EN_MASK);
}

/* BAUDR */
/**
 * @brief Set qspi clock divider.  
 * @param {qspi *} qspix:Pointer to a qspi structure,where can be QSPI0,QSPI1,QSPI2.
 * @param {uint16_t} div:divider value.
 * @return {void}
 */
void QSPI_Clock_Divider(qspi *qspix, uint16_t div)
{
    Bit_Clear(qspix->SSIENR, QSPI_SSIENR_SSIC_EN_MASK);
    qspix->BAUDR = div;
    Bit_Set(qspix->SSIENR, QSPI_SSIENR_SSIC_EN_MASK);
}

/* TXFTLR */
/**
 * @brief Set qspi transfer start fifo level
 * @param {qspi *} qspix:Pointer to a qspi structure,where can be QSPI0,QSPI1,QSPI2.
 * @param {uint8_t} value:Value range from 0 to 31.
 * @return {void}
 */
void QSPI_Change_Transfer_Start_FIFO_Level(qspi *qspix, uint8_t value)
{
    Bit_Clear(qspix->SSIENR, QSPI_SSIENR_SSIC_EN_MASK);
    Bit_Clear(qspix->TXFTLR, QSPI_TXFTLR_TXFTHR_MASK);
    Bit_Set(qspix->TXFTLR, QSPI_TXFTLR_TXFTHR(value));
    Bit_Set(qspix->SSIENR, QSPI_SSIENR_SSIC_EN_MASK);
}

/* SPI control 0 */
/**
 * @brief QSPI Change SPI Control Register
 * @param {qspi *} qspix:Pointer to a qspi structure,where can be QSPI0,QSPI1,QSPI2.
 * @param {qspi_transfer_type_te} type:QSPI address and instruction transfer format,
 *         where can be QSPI_Transfer_Type_TT0,QSPI_Transfer_Type_TT1,QSPI_Transfer_Type_TT2.
 * @param {qspi_address_length_te} addr_l:QSPI Address Length,where can be QSPI_Address_Length_0Bits,
 *        QSPI_Address_Length_4Bits,...,QSPI_Address_Length_60Bits.
 * @param {qspi_wait_cycles_te} dummy_cycle:QSPI Wait Cycles,where can be QSPI_Wait_Cycles_0,
 *        QSPI_Wait_Cycles_1,...,QSPI_Wait_Cycles_31.
 * @return {void}
 */
void QSPI_Change_SPI_Control_Register(qspi *qspix, qspi_transfer_type_te type, qspi_address_length_te addr_l, qspi_wait_cycles_te dummy_cycle)
{
    uint32_t tempReg = qspix->SPICTRLR0;

    Bit_Clear(tempReg, QSPI_SPICTRLR0_TRANS_TYPE_MASK);
    Bit_Set(tempReg, QSPI_SPICTRLR0_TRANS_TYPE(type));

    Bit_Clear(tempReg, QSPI_SPICTRLR0_ADDR_L_MASK);
    Bit_Set(tempReg, QSPI_SPICTRLR0_ADDR_L(addr_l));

    Bit_Clear(tempReg, QSPI_SPICTRLR0_WAIT_CYCLES_MASK);
    Bit_Set(tempReg, QSPI_SPICTRLR0_WAIT_CYCLES(dummy_cycle));

    Bit_Clear(qspix->SSIENR, QSPI_SSIENR_SSIC_EN_MASK);
    qspix->SPICTRLR0 = tempReg;
    Bit_Set(qspix->SSIENR, QSPI_SSIENR_SSIC_EN_MASK);
}

/* Status */
/**
 * @brief Get QSPI Status. 
 * @param {qspi *} qspix:Pointer to a qspi structure,where can be QSPI0,QSPI1,QSPI2.
 * @param {qspi_status_type_te} status:qspi status type,where can be QSPI_Status_BUSY,
 *        QSPI_Status_TFNF,...,QSPI_Status_TXE.
 * @return {FlagStatus} return Reset or Set.
 */
FlagStatus QSPI_Get_Status(qspi *qspix, qspi_status_type_te status)
{
    return (Bit_Read(qspix->SR, 1 << status) >> status);
}

/* DMA */
/**
 * @brief Set QSPI DMA Tx or Rx FIFO Trigger Threshold Level.
 * @param {qspi *} qspix:Pointer to a qspi structure,where can be QSPI0,QSPI1,QSPI2.
 * @param {qspi_dma_type_te} dma_type:where can be QSPI_DMA_RX or QSPI_DMA_TX.
 * @param {uint8_t} level:Value range from 0 to 7.
 * @return {void}
 */
void QSPI_DMA_Level(qspi *qspix, qspi_dma_type_te dma_type, uint8_t level)
{
    if (dma_type == QSPI_DMA_RX) {
        qspix->DMARDLR = level;
    } else {
        qspix->DMATDLR = level;
    }
}

/**
 * @brief QSPI DMA Enabel or Disable.
* @param {qspi *} qspix:Pointer to a qspi structure,where can be QSPI0,QSPI1,QSPI2.
 * @param {qspi_dma_type_te} dma_type:where can be QSPI_DMA_RX or QSPI_DMA_TX.
 * @param {FunctionState} state:Enabel or Disable.
 * @return {void}
 */
void QSPI_DMA_Cmd(qspi *qspix, qspi_dma_type_te dma_type, FunctionState state)
{
    Bit_Clear(qspix->DMACR, 1 << dma_type);
    Bit_Set(qspix->DMACR, state << dma_type);
}




/**
 * @brief Sets default values for the xip configuration structure variable pointer.
 * @param {xip_config_ts *} xip_config:Pointer to a xip configuration structure that
 *                          contains the configuration information for the specified
 *                          XIP peripheral.
 * @return {void}
 */
void XIP_GetDefault(xip_config_ts *xip_config)
{
    xip_config->frame_format = XIP_Frame_Format_Quad_SPI;
    xip_config->trans_type   = XIP_Transfer_Type_TT0;
    xip_config->inst_en      = XIP_Instruction;
    xip_config->inst_l       = XIP_Instruction_Length_8Bits;
    xip_config->addr_l       = XIP_Address_Length_24Bits;
    xip_config->dummy_cycles = XIP_Wait_Cycles_8;

    xip_config->mode_bits_en         = Disable;
    xip_config->mode_bits_l          = XIP_ModeBits_2;
    xip_config->continus_transfer_en = Enable;
    xip_config->pre_fetch_en         = Disable;

    xip_config->inst_value                 = 0x6BU;
    xip_config->wrap_inst_value            = 0x6BU;
    xip_config->mode_bits_value            = 0x00U;
    xip_config->continus_transfer_time_out = 0x0FU;
}

/**
 * @brief Initializes the qspi peripheral xip function according to the specified parameters in the xip_config.
 * @param {qspi *} qspix:Pointer to a qspi structure,where can be QSPI0,QSPI1,QSPI2.
 * @param {xip_config_ts *} xip_config:Pointer to a xip configuration structure that
 *                          contains the configuration information for the specified
 *                          XIP peripheral.
 * @return {void}
 */
void XIP_Init(qspi *qspix, xip_config_ts *xip_config)
{
    uint32_t tempReg = 0;
    Bit_Clear(qspix->SSIENR, QSPI_SSIENR_SSIC_EN_MASK); /* must disable QSPI module */

    /* instruction length can be writed only when xip transfers will have instruction phase */
    if (xip_config->inst_en == XIP_Instruction) {
        Bit_Set(tempReg, QSPI_XIPCR_INST_EN(xip_config->inst_en));
        Bit_Set(tempReg, QSPI_XIPCR_INST_L(xip_config->inst_l));
    }

    Bit_Set(tempReg, QSPI_XIPCR_FRF(xip_config->frame_format));
    Bit_Set(tempReg, QSPI_XIPCR_TRANS_TYPE(xip_config->trans_type));
    Bit_Set(tempReg, QSPI_XIPCR_ADDR_L(xip_config->addr_l));
    Bit_Set(tempReg, QSPI_XIPCR_WAIT_CYCLES(xip_config->dummy_cycles));
    Bit_Set(tempReg, QSPI_XIPCR_CONT_XFER_EN(xip_config->continus_transfer_en));
    Bit_Set(tempReg, QSPI_XIPCR_XIP_PREFETCH_EN(xip_config->pre_fetch_en));

    if (xip_config->mode_bits_en == Enable) {
        Bit_Set(tempReg, QSPI_XIPCR_MD_BITS_EN(xip_config->mode_bits_en));
        Bit_Set(tempReg, QSPI_XIPCR_XIP_MBL(xip_config->mode_bits_l));

        Bit_Clear(qspix->XIPMBR, QSPI_XIPMBR_XIP_MD_BITS_MASK);
        Bit_Set(qspix->XIPMBR, QSPI_XIPMBR_XIP_MD_BITS(xip_config->mode_bits_value));
    }

    qspix->XIPCR    = tempReg;
    qspix->XIPIIR   = xip_config->inst_value;
    qspix->XIPWIR   = xip_config->wrap_inst_value;
    qspix->XIPCTTOR = xip_config->continus_transfer_time_out;

    Bit_Set(qspix->SSIENR, QSPI_SSIENR_SSIC_EN_MASK); /* must disable QSPI module */
}


/**
 * @brief QSPI Standard Full Duplex Mode Initialization.
 * @param {qspi *} qspix:Pointer to a qspi structure,where can be QSPI0,QSPI1,QSPI2.
 * @param {uint16_t} sck_dv:qspi module clock frequency division factor.
 * @return {void}
 */
void QSPI_STD_Init(qspi* qspix,uint16_t sck_dv)
{
    qspi_config_ts qspi_config;
    QSPI_GetDefault(&qspi_config);
    qspi_config.sckdv = sck_dv;
    QSPI_Init(qspix,&qspi_config);

}

/**
 * @brief QSPI Standard Full Duplex Write read Function.
 * @param {qspi *} qspix:Pointer to a qspi structure,where can be QSPI0,QSPI1,QSPI2.
 * @param {uint8_t} data:The data you want to transfer.
 * @return {uint32_t} return qspi read value.
 */
uint8_t QSPI_STD_WriteRead(qspi *qspix, uint32_t data)
{
    for (; QSPI_Get_Status(qspix, QSPI_Status_TFE) == QSPI_STATUS_TxFIFO_NOT_EMPTY; )  // Wait Transmit FIFO Empty
    {
        asm("nop");
    }
    qspix->DR = data;	 	  		                    // sent data
    for (; QSPI_Get_Status(qspix, QSPI_Status_RFNE) == QSPI_STATUS_RxFIFO_EMPTY; ) // Wait Receive FIFO is not Empty
    {
        asm("nop");
    }
    return ((uint8_t)qspix->DR);          		        // return receive data

}
